package com.ruihu.admin.module.support.task.service;

import com.ruihu.admin.common.exception.BusinessException;
import com.ruihu.admin.module.support.task.domain.DyTaskInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.time.ZoneId;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

@Service
@Slf4j
public class DefaultDyTaskService implements DyTaskService {

    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;

    Map<Long, DyTaskInfo> taskMap = new ConcurrentHashMap<>();

    @Override
    public boolean scheduleTask(Long interfaceLogId, DyTask dyTask, String cron) {
        Method method = ReflectionUtils.findMethod(dyTask.getClass(), "run");
        if (Objects.isNull(method)) {
            throw new BusinessException("无法获取run方法, 无法执行任务");
        }

        try {
            CronTask cronTask = new CronTask(createRunnable(dyTask, method), new CronTrigger(cron, TimeZone.getTimeZone(ZoneId.systemDefault())));
            DyTaskInfo taskInfo = new DyTaskInfo(cronTask);
            ScheduledFuture<?> future = taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
            taskInfo.setFuture(future);

            this.cancelTask(interfaceLogId);

            this.taskMap.put(interfaceLogId, taskInfo);
        } catch (Throwable e) {
            log.info("配置异常:{}", e.getMessage());
        }

        return false;
    }

    private Runnable createRunnable(DyTask dyTask, Method method) {
        Method invocableMethod = AopUtils.selectInvocableMethod(method, dyTask.getClass());
        return new ScheduledMethodRunnable(dyTask, invocableMethod);
    }

    @Override
    public boolean cancelTask(Long interfaceLogId) {
        if (Objects.isNull(interfaceLogId)) {
            return false;
        }
        DyTaskInfo task = taskMap.remove(interfaceLogId);
        if (task != null && task.getFuture() != null) {
            task.getFuture().cancel(true);
            log.info("Task {} cancel.", interfaceLogId);
            return true;
        }
        return false;
    }

    @Override
    public boolean taskIsRunning(Long interfaceLogId) {
        if (Objects.isNull(interfaceLogId)) {
            return false;
        }
        DyTaskInfo task = taskMap.get(interfaceLogId);
        if (task != null && task.getFuture() != null) {
            return !task.getFuture().isDone() && !task.getFuture().isCancelled();
        }
        return false;
    }
}
